SDL、WebAssembly 杂记
SDL
SDL(Simple DirectMedia Layer)是一个跨平台开发库,旨在通过 OpenGL 和 Direct3D 提供对音频、键盘、鼠标、操纵杆和图形硬件的低级访问。可以在大部分的开源库看到它的身影,因为它体积小,且轻便。非常方面时候简单的跨平台开发,例如通过其在屏幕上显示画面。
例如 FFmpeg 项目中的 ffplay 就是使用其在窗口上显示视频画面的。这里我们简单的在 Windows 下写一个非常简单的示例。首先下载二进制库文件 SDL2-devel-2.30.1-VC.zip
。
在 Windows 下,我们需要定义宏 SDL_MAIN_HANDLED
(在SDL_main.h
中有简单的说明)。
#include <iostream>
#include <SDL.h>
constexpr int ScreenWidth = 640;
constexpr int ScreenHeight = 480;
int main(int argc, char const *argv[]) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) { //初始化 SDL
std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
return -1;
}
auto window = SDL_CreateWindow("SDL Demo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, ScreenWidth,
ScreenHeight, SDL_WINDOW_SHOWN);
while (true) {
SDL_Event e;
SDL_PollEvent(&e);
if (e.type == SDL_QUIT) break;
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
这是一个最简单的示例,它会创建一个窗口,然后里面都是黑的。
libSDL2pp
libSDL2pp 为 SDL2 提供 C++ 绑定/包装器,这里仅提及,不做介绍。
WebAssembly
Emscripten 是一个完整的 WebAssembly 编译器工具链。最开始知道这项技术的时候,第一感觉就是 wasm 为 C/C++ 开发人员进入Web开发打开了一个新的方向。
最开始接触到的是 Qt for WebAssembly,当时以为这样就能够即使不会前端技术,也能在Web上开发APP了,但是实验下来,貌似情况不是这样的。一方面是 Qt for WebAssembly 实现上还不完善,存在着一些至少在我目前认为是莫名其妙的BUG。另外一个,打包出来的文件,依旧非常大,那么这就以为的在外网部署 不太现实。只能在内网使用,或者是特定场合的重量级应用,使用者愿意花五六秒种去等待,也不在意流量。
我是在 lvgl 中看到 sdl 也能在 WebAssembly 中使用的,因为 lvgl 在 WebAssembly 中,也是使用 sdl 作为渲染接口。
环境搭建
需要先安装 Python。
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
Set-ExecutionPolicy -Scope CurrentUser remotesigned
.\emsdk.ps1 install latest
.\emsdk.ps1 activate latest
# Activate PATH and other environment variables in the current terminal
.\emsdk_env.ps1
最简单示例
这里我们写一个最简单的示例代码:
#include <emscripten.h>
#include <emscripten/bind.h>
#include <iostream>
#include <string>
int add(int a, int b) {
return a + b;
}
std::string add(const std::string &a, const std::string &b) {
return a + b;
}
EM_JS(int, javaScriptAdd, (int a, int b), {
console.log("add a+b in javascript.");
return a + b;
});
EMSCRIPTEN_BINDINGS(my_module) {
emscripten::function("add", emscripten::select_overload<int(int, int)>(&add));
emscripten::function("stringAdd",
emscripten::select_overload<std::string(const std::string &, const std::string &)>(&add));
}
int main(int argc, char const *argv[]) {
std::cout << "hello, emscripten!" << std::endl;
std::cout << "a+b=" << javaScriptAdd(5, 6) << std::endl;
int x = EM_ASM_INT({
console.log(`add ${$0} + ${$1} in inline javascript.`);
return $0 + $1;
}, 7, 8);
std::cout << "7+8=" << x << std::endl;
return 0;
}
然后我们编译:
em++ .\main.cpp
会生成 a.out.js
和a.out.wasm
两个文件。a.out.wasm
包含编译完成的 C/C++ 代码,a.out.js
包含加载 wasm 文件的代码,和运行时支持。
在生成的 JavaScript 文件中,会使用到一个名为 Module 的全局对象,我们可以在运行该脚本时,提前定义好改对象,用以控制 wasm 的执行状态。如果我们指定编译输出文件的后缀为 .html
:em++ .\main.cpp -o main.html
,那么除了上述两个文件,Emscripten 还会为我们生成一个 main.html
文件,这是 Emscripten 提供的一个默认文件,文件模板为 D:\emsdk\upstream\emscripten\src\shell.html
。
这里我们实现一个最近的模板文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Emscripten Demo</title>
</head>
<body>
<script>
var Module = {
print: function (text) {
console.log(text);
},
printErr: function (text) {
console.error(text);
},
onRuntimeInitialized: function () {
console.log("2+3=", Module.add(2, 3));
console.log("'2'+'3'='", Module.stringAdd('2', '3'));
}
};
</script>
{{{ SCRIPT }}}
<script>
</script>
</body>
</html>